package com.study.sbsummary.config.aop.request;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.study.sbsummary.config.constant.MetricConstant;
import com.study.sbsummary.config.database.shadow.ShadowConstants;
import com.study.sbsummary.context.RequestContext;
import com.study.sbsummary.utils.MetricUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * aop 全局打点，针对controller层的监控
 */
@Component
@Aspect
@Slf4j
public class ControllerAop {

    @Value("${spring.application.name}")
    private String applicationName;


    /**
     * 配置切入点
     */
    @Pointcut("execution(* com.study.sbsummary.controller..*.*(..))")
    public void aspect() {
    }

    @Around("aspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 起始时间
        long start = System.currentTimeMillis();
        String url = logRequest(joinPoint);
        try {
            return recordMetricsAndExecute(joinPoint, start, url);
        } catch (Throwable e) {
            log.error("controller around error: ", e);
            throw e;
        } finally {
            RequestContext.clear();
            log.info("request: {} cost: {}", url, (System.currentTimeMillis() - start));
        }
    }

    private Object recordMetricsAndExecute(ProceedingJoinPoint joinPoint, long start, String url) throws Throwable {

        // metric key: 类名 + 方法名
        String clazzName = joinPoint.getTarget().getClass().getSimpleName();
        String node = (clazzName + "_" + joinPoint.getSignature().getName()).toLowerCase();
        boolean isInitBinderMethod = joinPoint.getSignature().getName().equalsIgnoreCase("initBinder");
        if (!isInitBinderMethod) {
            // qps打点: metric
            MetricUtils.metricQps(applicationName, MetricConstant.BIZ, node);
        }
        Object result = joinPoint.proceed();
        if (!isInitBinderMethod) {
            // 响应时间打点: metric
            MetricUtils.metricLatency(applicationName, MetricConstant.BIZ, node, start);
        }
        if (this.getClazzList().contains(clazzName)) {
            // 记录响应内容
            log.info("response: url={}, content={}, threadId={}", url, JSON.toJSONString(result), Thread.currentThread().getId());
        }
        return result;
    }

    private List<String> getClazzList() {
        return Lists.newArrayList("DocInnerController");
    }

    private String logRequest(ProceedingJoinPoint joinPoint) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//这个RequestContextHolder是Springmvc提供来获得请求的东西
        if (ObjectUtil.isNotEmpty(requestAttributes) && ObjectUtil.isNotEmpty(((ServletRequestAttributes) requestAttributes).getRequest())) {
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
            checkOnlineTest(request);
            String url = request.getRequestURL().toString();
            log.info("request: url={}, method={}, ip={}, params={}, body={}, threadId={}",
                    url, request.getMethod(), request.getRemoteAddr(), request.getQueryString(),
                    JSON.toJSONString(joinPoint.getArgs()), Thread.currentThread().getId());
            return url;
        }
        return joinPoint.getSignature().getName();
    }

    private void checkOnlineTest(HttpServletRequest request) {
        RequestContext.onlineTest(request.getParameterMap().containsKey(ShadowConstants.PRESSURE_TEST_KEY));
    }
}
